Igual que los menús, se pueden construir durante la ejecución o a partir de un fichero de recursos
Cada IDE tiene un editor gráfico de diálogos, no obstante, es bueno entender la base que hay detrás.
Fichero decabecera:
// File: ids.h /* Identificadores */ /* Identificadores de comandos */ #define TEXTO 100 #define CM_DIALOGO 101 #define CM_DIALOGO_PARAM 102
// File: ids.h
/* Identificadores */
/* Identificadores de comandos */
#define TEXTO 100
#define CM_DIALOGO 101
#define CM_DIALOGO_PARAM 102Fichero de recursos:
//File win004.rc #include <windows.h> #include "ids.h" BarraMenu MENU BEGIN POPUP "&Principal" BEGIN MENUITEM "&Diálogo", CM_DIALOGO MENUITEM "&Con parámetro", CM_DIALOGO_PARAM END END DialogoPrueba DIALOG 0, 0, 118, 48 // id tipo posición y dimensiones STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION // Dialog Styles DS_ Window Styles WS_ CAPTION "Diálogo de prueba" // Título del diálogo FONT 8, "Helv" // tipografía del diálogo BEGIN CONTROL "Mensaje de prueba", TEXTO, "static", SS_LEFT | WS_CHILD | WS_VISIBLE, 8, 9, 84, 8 CONTROL "Aceptar", IDOK, "button", BS_PUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 56, 26, 50, 14 END
//File win004.rc
#include <windows.h>
#include "ids.h"
BarraMenu MENU
BEGIN
POPUP "&Principal"
BEGIN
MENUITEM "&Diálogo", CM_DIALOGO
MENUITEM "&Con parámetro", CM_DIALOGO_PARAM
END
END
DialogoPrueba DIALOG 0, 0, 118, 48 // id tipo posición y dimensiones
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION // Dialog Styles DS_ Window Styles WS_
CAPTION "Diálogo de prueba" // Título del diálogo
FONT 8, "Helv" // tipografía del diálogo
BEGIN
CONTROL "Mensaje de prueba", TEXTO, "static", SS_LEFT | WS_CHILD | WS_VISIBLE, 8, 9, 84, 8
CONTROL "Aceptar", IDOK, "button", BS_PUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 56, 26, 50, 14
ENDUn menú “principal” con dos opciones “diálogo” y “con parámetro”.
El identificador “DialogoPruebas”, DIALOG, las coordenadas y dimensiones.
Estilos, las constantes para definir los estilos de ventana, que comienzan con WS_ (window style), puedes verlos con detalle en la sección de constantes “estilos de ventana”. Y los estilos de diálogos, que comienzan con DS_ (dialog style), en “estilos de diálogo”.
En la zona de controles hemos incluido un texto estático y un botón.
El control static sirve para mostrar textos (etiquetas) o rectángulos (adornos)
CONTROL indica un elemento de diálogo.
A continuación introducimos el texto que mostrar.
Como ID del control ponemos -1 pues no se usa en la aplicación.
Después la clase de control “static” seguida por el estilo, las coordenadas X e Y, la anchura y la altura
Sirve para comunicar con el usuario con los mensajes de comandos como en los menús
CONTROL, el texto que mostrar, el ID que, se combina con el mensaje WM_COMMAND cuando el usuario hace clic en el botón.
La etiqueta IDOK está definida en el fichero Windows.h
La clase de control es “button” y luego el estilo
BS_PUSHBUTTON crea un botón corriente que envía un mensaje WM_COMMAND a su ventana padre cuando el usuario selecciona el botón.
BS_CENTER centra el texto horizontalmente en el área del botón.
WS_CHILD crea el control como una ventana hija.
WS_VISIBLE crea una ventana inicialmente visible.
WS_TABSTOP define un control que puede recibir el foco del teclado cuando el usuario pulsa la tecla TAB. Presionando la tecla TAB, el usuario mueve el foco del teclado al siguiente control con el estilo WS_TABSTOP.
Coordenadas X e Y del control seguidas de la anchura y la altura.
Un diálogo es una ventana, las ventanas tienen procedimiento de ventanas, los diálogos tienen procedimiento de diálogo que procesan los mensajes que les son enviados.
BOOL CALLBACK DialogProc(
HWND hwndDlg, // manipulador del diálogo
UINT uMsg, // mensaje
WPARAM wParam, // 1er parámetro del mensaje
LPARAM lParam // 2o parámetro del mensaje
);BOOL CALLBACK DialogProc(
HWND hwndDlg, // manipulador del diálogo
UINT uMsg, // mensaje
WPARAM wParam, // 1er parámetro del mensaje
LPARAM lParam // 2o parámetro del mensaje
);La diferencia con el procedimiento de ventana que ya hemos visto está en el tipo de valor de retorno, que en el caso del procedimiento de diálogo es de tipo booleano.
Excepto en la respuesta al mensaje WM_INITDIALOG, el procedimiento de diálogo debe retornar con un valor no nulo si procesa el mensaje y cero si no lo hace. Cuando responde a un mensaje WM_INITDIALOG, el procedimiento debe retornar cero si llama a la función SetFocus para poner el foco a uno de los controles del cuadro de diálogo. En otro caso, debe retornar un valor distinto de cero, y el sistema pondrá el foco en el primer control del diálogo que pueda recibirlo.
BOOL CALLBACK DlgProc(HWND, UINT, WPARAM, LPARAM);
BOOL CALLBACK DlgProc(HWND, UINT, WPARAM, LPARAM);El mensaje WM_INITDIALOG lo usaremos para inicializar el diálogo antes de mostrarlo por pantalla. Debemos retornar un valor distinto de cero si no llamamos a SetFocus
El mensaje WM_COMMAND, procede del único botón del diálogo, cerraremos el diálogo llamando a la función EndDialog y retornaremos con un valor distinto de cero.
En cualquier otro caso retornamos FALSE, ya que no estaremos procesando el mensaje.
BOOL CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg) /* manipulador del mensaje */
{
case WM_INITDIALOG:
return TRUE;
case WM_COMMAND:
EndDialog(hDlg, FALSE);
return TRUE;
}
return FALSE;
}BOOL CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg) /* manipulador del mensaje */
{
case WM_INITDIALOG:
return TRUE;
case WM_COMMAND:
EndDialog(hDlg, FALSE);
return TRUE;
}
return FALSE;
}Lanzamos el diálogo desde el menú, el menú está en la pantalla así que la función de procesar pantalla WindowProcedure:
LRESULT CALLBACK WindowProcedure(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
static HINSTANCE hInstance;
static veces;
switch (msg) /* manipulador del mensaje */
{
case WM_CREATE:
hInstance = ((LPCREATESTRUCT)lParam)->hInstance;
return 0;
break;
case WM_COMMAND:
switch(LOWORD)
{
case CM_DIALOGO:
DialogBox(hInstance, “DialogoPrueba”, hwnd, DlgProc);
break;
case CM_DIALOGO_PARAM:
veces++;
DialogBoxParam(hInstance, “DialogoPrueba”, hwnd, DlgProc2, veces);
break;
}
break;
case WM_DESTROY:
PostQuitMessage(0); // envía un mensaje WM_QUIT a la cola de mensajes
break;
default: // para los mensajes de los que no nos ocupamos
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}LRESULT CALLBACK WindowProcedure(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
static HINSTANCE hInstance;
static veces;
switch (msg) /* manipulador del mensaje */
{
case WM_CREATE:
hInstance = ((LPCREATESTRUCT)lParam)->hInstance;
return 0;
break;
case WM_COMMAND:
switch(LOWORD)
{
case CM_DIALOGO:
DialogBox(hInstance, “DialogoPrueba”, hwnd, DlgProc);
break;
case CM_DIALOGO_PARAM:
veces++;
DialogBoxParam(hInstance, “DialogoPrueba”, hwnd, DlgProc2, veces);
break;
}
break;
case WM_DESTROY:
PostQuitMessage(0); // envía un mensaje WM_QUIT a la cola de mensajes
break;
default: // para los mensajes de los que no nos ocupamos
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}hInstance es una variable estática para tener siempre un manipulador a la instancia actual. Se le da valor en WM_CREATE que es un mensaje del inicio del programa. El mensaje WM_CREATE tiene como parámetro en lParam un puntero a una estructura CREATESTRUCT que contiene información sobre la ventana. En nuestro caso sólo nos interesa el campo hInstance.
La otra novedad es la llamada a la función DialogBox, que crea el diálogo usando los parámetros:
Manipulador de instancia de aplicación, obtenida con el mensaje WM_CREATE.
Identificador del diálogo, el nombre del diálogo entre comillas.
Manipulador de la ventana padre del diálogo.
La función de procedimiento del diálogo.
Macro DialogBoxParam funciona igual que la otra macro pero permite enviar un parámetro más al procedimiento de diálogo. El parámetro es de tipo lParam con lo que puede ser entero o un puntero y tanto es de entrada como de salida.
En el ejemplo el diálogo con argumento lleva un contador estático y lo pasa a la función para que muestre el valor por pantalla.
static int veces;
(...)
case WM_COMMAND:
switch(LOWORD(wParam))
{
case CM_DIALOGO:
DialogBox(hInstance, "DialogoPrueba", hwnd, DlgProc);
break;
case CM_DIALOGO2:
veces++;
DialogBoxParam(hInstance, "DialogoPrueba2", hwnd, DlgProc2, veces);
break;
}
break;
(...)static int veces;
(...)
case WM_COMMAND:
switch(LOWORD(wParam))
{
case CM_DIALOGO:
DialogBox(hInstance, "DialogoPrueba", hwnd, DlgProc);
break;
case CM_DIALOGO2:
veces++;
DialogBoxParam(hInstance, "DialogoPrueba2", hwnd, DlgProc2, veces);
break;
}
break;
(...)y la función de proceso del segundo diálogo:
BOOL CALLBACK DlgProc2(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
char texto[25];
switch (msg) /* manipulador del mensaje */
{
case WM_INITDIALOG:
sprintf(texto, "Veces invocado: %d", (int)lParam);
SetWindowText(GetDlgItem(hDlg, TEXTO), texto);
return TRUE;
case WM_COMMAND:
EndDialog(hDlg, FALSE);
return TRUE;
}
return FALSE;
}BOOL CALLBACK DlgProc2(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
char texto[25];
switch (msg) /* manipulador del mensaje */
{
case WM_INITDIALOG:
sprintf(texto, "Veces invocado: %d", (int)lParam);
SetWindowText(GetDlgItem(hDlg, TEXTO), texto);
return TRUE;
case WM_COMMAND:
EndDialog(hDlg, FALSE);
return TRUE;
}
return FALSE;
}SetWindowText cambia el título de una ventana y también el texto de un control estático.
Es la forma más adecuada de comunicar variables entre la aplicación y los procedimientos de diálogo sin tener que recurrir a variables globales.